require 'MD5'

class User < ActiveRecord::Base

  has_many :orders
  has_many :addresses
  belongs_to :cart

  validates_length_of :name, :in => 4..20
  validates_uniqueness_of :name
  validates_presence_of :first_name,
                        :surname

  validates_inclusion_of :gender, :in => [0, 1]

  validates_format_of :email,
                      :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
                      :message => 'is geen geldig e-mailadres.'

  validates_format_of :name,
                      :with => /^[a-z0-9\-_]+$/i,
                      :message => 'mag alleen de volgende tekens bevatten: a t/m z, 0 t/m 9, "_" en "-".'

  validate :validate_password


  # Initialize defaults.
  def before_create
    self.activated       = false
    self.activation_code = User.generate_hash(40)
    self.balance         = 0
    self.is_admin        = 0
  end

  
  # Encrypt het wachtwoord als dat veranderd is.
  def before_save
    write_attribute(:password, User.encrypt_password(password)) if password_changed?
  end


  # Valideer het wachtwoord alleen als dat gewijzigd is.
  def validate_password
    if password_changed?

      # Een wachtwoord mag niet korter zijn dan 6 tekens.
      if password.blank? or password.size < 6
        self.errors.add(:password, ' moet minimaal uit 6 tekens bestaan.' )

      # Een wachtwoord mag niet langer zijn dan 25 tekens
      elsif password.size > 25
        self.errors.add(:password, ' mag maximaal uit 25 tekens bestaan.' )

      # Elk wachtwoord moet minimaal 2 letters en 2 getallen bevatten.
      else
        digit = 0
        alpha = 0

        password.each_char do |c|
          if c.match /^[0-9]$/
            digit += 1
          elsif c.match /^[a-z]$/i
            alpha += 1
          end
        end

        if alpha < 2 || digit < 2
          self.errors.add(:password, ' moet minimaal 2 getallen en 2 letters bevatten' )
        end
      end
    end
  end


  # Controleer of deze gebruiker een vrouw is.
  def gender_female?
    gender.to_i == 1
  end

  # Controleer of deze gebruiker een man is.
  def gender_male?
    gender.to_i == 0
  end

  # Genereer een nieuwe token voor deze gebruiker en sla deze op.
  def refresh_token
    
    # Door het id van deze gebruiker aan de hash te plakken is het zeker
    # dat de token uniek is, zelfs als er bij twee verschillende gebruiker
    # dezelfde hash gegenereerd wordt (de kans hierop is echter zeer klein).
    new_token = self.id.to_s + '-' + User.generate_hash(40)

    update_attribute(:token, new_token)

    return new_token
  end

  # Geef het winkelwagentje van deze gebruiker. Als er geen winkelwagentje
  # gevonden kan worden, maak dan een nieuw winkelwagentje aan.
  def retrieve_cart
    if self.cart == nil
      new_cart = Cart.create

      self.cart    = new_cart

      self.save!

      return new_cart
    else  
      return self.cart
    end

  end


  
  # Controleer of het opgegeven wachtwoord overeen komt met het
  # opgeslagen wachtwoord.
  def check_password(input)
    hash, salt = password.split(':')

    return MD5.new(input.to_s + salt.to_s).hexdigest.to_s == hash
  end



  # Zoek de gebruiker die bij de opgegevens gebruikersnaam hoort
  # en controleer of het wachtwoord klopt.
  def self.login(name, password)
    user = User.first(:conditions => {:name => name})

    if user and user.check_password(password) and user.activated
      user.refresh_token

      return user
    else
      return nil
    end
  end



  # Zoek een gebruiker die geactiveerd moet worden en activeer hem als dat kan.
  def self.activate_by_code(activation_code)
    user = User.find_by_activation_code(activation_code)

    if user and !user.activated
      user.activated       = true
      user.activation_code = nil
      user.save!

      return user
    else
      return nil
    end
  end


  # Encrypt een wachtwoord d.m.v MD5.
  def self.encrypt_password(password)
    salt = User.generate_hash(20)

    return MD5.new(password + salt).hexdigest.to_s + ':' + salt
  end


  # Genereer een random hash van een lengte n die bestaat uit kleine letters,
  # hoofdletters en getallen. Deze methode wordt gebruikt om tokens
  # en acivatiecodes te genereren.
  def self.generate_hash(n)
    chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a

    Array.new(n){chars[rand(chars.size)]}.join
  end

end
